Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: do not cancel stripe subscription in case of failed payment #8285

Merged
merged 6 commits into from
Jun 15, 2023

Conversation

igorlesnenko
Copy link
Contributor

@igorlesnenko igorlesnenko commented May 26, 2023

Fix #8166

I changed the following subscriptions settings on stripe, it should not affect prod, as we currently terminate subscriptions from the code on failed payments anyway:

image image

How to test:

Case 1:

Case 2:

  • make sure the webhook is active
  • create a new org
  • Uncomment: // trial_end: toEpochSeconds(new Date(Date.now() + 1000 * 10)), in createTeamSubscription
  • Upgrade with 4000 0000 0000 0341 card (will fail after trial)
  • Wait few minutes
  • Go to the subscription https://dashboard.stripe.com/test/subscriptions
  • Find draft invoice (or wait for it to be appeared)
  • Finalize the invoice — edit and click charge (to avoid waiting 1 hour)
  • See that teams were locked
  • See that the subscription went to past due state but not canceled
  • Update card on organization: 4242 4242 4242 4242
  • See all teams unlocked, subscription became active, no new subscription created, invoice is paid

@igorlesnenko igorlesnenko requested a review from tianrunhe May 26, 2023 15:52
Copy link
Contributor

@tianrunhe tianrunhe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't quite follow why we are still canceling subscriptions on first failed payment? I thought the test case 1 is what's happening today and we want to change against it?

const subscriptionObject = await manager.retrieveSubscription(stripeSubscriptionId)

if (subscriptionObject.status === 'incomplete' || subscriptionObject.status === 'canceled') {
// Terminate subscription if it is the first failed payment, or it is already canceled
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why terminate the subscription on 1st failed payment?

Copy link
Contributor Author

@igorlesnenko igorlesnenko Jun 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tianrunhe I keep what is happening today only for case 1:
User setup the subscription for the first time, he adds a card but then almost immediately first payment fails.

I keep this for two reasons:

  • I think it is safe to cancel subscription in this case.
  • Stripe documentation suggest to cancel such subscriptions if no payment was made within 23 hours.
    To not handle this 23 hours case specifically I think it is easier to just cancel right now:

Payment window
Customers have about 23 hours to make a successful payment. The subscription remains in status incomplete and the invoice is open during this time. If your customer pays the invoice, the subscription updates to active and the invoice to paid. If they don't make a payment, the subscription updates to incomplete_expired and the invoice becomes void.

This window exists because your customer usually makes the first payment for a subscription while on-session. If the customer returns to your application after 23 hours, create a new subscription for them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added more comments in code. What do you think?

@igorlesnenko igorlesnenko requested a review from tianrunhe June 2, 2023 11:43
@tianrunhe
Copy link
Contributor

tianrunhe commented Jun 5, 2023

I don't like that it cancels the subscription and locks the team immediately, for 3DS card (test card number: 4000002500003155 from here). We have customer contacted us for this use case before.

@igorlesnenko
Copy link
Contributor Author

@tianrunhe I think the case with 3D secure card is no different from Case 1 in the issue description.
If you enter non-valid card, payment fails and we lock the org.

3D secure card will always fail because we don't have 3D secure card support implemented.
For 3D secure an additional window with authentication needs to be shown.

For 1st payment it is safe to cancel subscription, because the initial issue is only about canceling already existing subscription with existing past payments — "unpaid balances failing to carry forward"

What do you think?

Copy link
Contributor

@tianrunhe tianrunhe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All good then!

@igorlesnenko igorlesnenko requested a review from mattkrick June 7, 2023 22:18
@mattkrick
Copy link
Member

Ooooo this is some tricky stuff, thank you for tackling it!

So as I understand the business logic, we cancel the subscription in stripe if it has been alive for < 1 day (failed payment, delayed failed payment).

If a subsequent payment fails (e.g. CC canceled/expired) then we lock them out of their team, but keep the subscription running. That means if it takes them a month to update their CC, they owe us for that month even though they haven't used us. Is that OK? Not saying it's good or bad, just want to make sure that's the intended effect.

Code Review

We use stripeSubscriptionId as a proxy for an org being paid, so we'll need to make sure that across the codebase we no longer do that. For example we need to fix

isPaid: Boolean(org.stripeSubscriptionId),

@igorlesnenko
Copy link
Contributor Author

@mattkrick

So as I understand the business logic, we cancel the subscription in stripe if it has been alive for < 1 day (failed payment, delayed failed payment).

Yes, we cancel subscription if it is in incomplete state and payment fails (no other payments were made in past)

If a subsequent payment fails (e.g. CC canceled/expired) then we lock them out of their team, but keep the subscription running. That means if it takes them a month to update their CC, they owe us for that month even though they haven't used us. Is that OK? Not saying it's good or bad, just want to make sure that's the intended effect.

Yes, this is the expected behavior at this time. We want to improve by giving some period before locking the org, andonly then cancel the subscription: #8341

We use stripeSubscriptionId as a proxy for an org being paid, so we'll need to make sure that across the codebase we no longer do that. For example we need to fix

Thanks! This seems to be the only case. Fixed!

@igorlesnenko igorlesnenko requested review from mattkrick and removed request for mattkrick June 13, 2023 09:44
@mattkrick mattkrick merged commit 74b8aa5 into master Jun 15, 2023
@mattkrick mattkrick deleted the stripe-no-cancel branch June 15, 2023 18:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Billing: Failed Payments should not immediately cancel Stripe Subscriptions
3 participants